// ========================================================================
// Copyright (c) 2022 ChangYou, All rights reserved.
// http://www.changyou.com/
// 
// Filename:    FoliageWorldMaps.cs
// Time:        2022-08-22 20:40:19
// Author:      xiangjinbao
// Email:       xiangjinbao@cyou-inc.com
// Version:     v2022.0.1
// Description: FoliageWorldMaps
// ========================================================================

using UnityEngine;
using UnityEngine.Serialization;

namespace ProjectTW.Foliage
{
    /// <summary>
    /// 世界地图图信息，内部暂时只包含Hight信息
    /// </summary>
    [System.Serializable]
    public class FoliageWorldMaps
    {
        private const int Height_Resolution = 2048;

        [SerializeField]
        private FoliageHeightMap m_HeightMap;
        [SerializeField]
        private FoliageChunk m_FoliageInstance;

        public FoliageHeightMap HeightMap
        {
            get
            {
                if (m_HeightMap == null || m_HeightMap.Map == null)
                {
                    FoliageMapUtility.GenerateWorldMaps(m_FoliageInstance);
                    UpdateHeightsAndNormals(true);
                }

                return m_HeightMap;
            }
            internal set
            {
                m_HeightMap = value;
            }
        }

        public FoliageHeightMap HeightMapFast
        {
            get
            {
                return m_HeightMap;
            }
        }

        public bool Dirty
        {
            get
            {
                return m_HeightMap.Dirty;
            }
        }

        public FoliageWorldMaps(Texture2D heightMap, FoliageChunk foliageInstance, bool updateHeights)
        {
            this.m_HeightMap = new FoliageHeightMap(heightMap, foliageInstance);
            this.m_FoliageInstance = foliageInstance;

            if (updateHeights)
            {
                UpdateHeightsAndNormals(true);
            }

            Save();
        }

        /// <summary>
        /// Normalize a world height into a converted height.
        /// </summary>
        /// <param name="worldHeight"></param>
        /// <returns></returns>
        public static Vector2b NormalizeHeight(float worldHeight)
        {
            return NormalizeHeight(worldHeight, FoliageManager.Instance);
        }

        /// <summary>
        /// Normalize a world height into a converted height.
        /// </summary>
        /// <param name="worldHeight"></param>
        /// <returns></returns>
        public static Vector2b NormalizeHeight(float worldHeight, FoliageManager manager)
        {
            Vector2b heights;

            float h = ((worldHeight - manager.m_MainPosition.y) / Height_Resolution) * 65535;

            heights.x = (byte) (h / 256);
            heights.y = (byte) (h - (heights.x * 256));

            return heights;
        }

        /// <summary>
        /// Update height on a certain range.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="z"></param>
        /// <param name="sizeX"></param>
        /// <param name="sizeZ"></param>
        public void UpdateHeightsAndNormals(float x, float z, int sizeX, int sizeZ, bool isSave)
        {
            int mapWidth = m_FoliageInstance.AttachedFoliageInstance.FoliageAreaResolutionIntegral;
            float resMultiplier = m_FoliageInstance.AttachedFoliageInstance.TransformCordsMultiplier;

            x = Mathf.Clamp(x, 0, mapWidth);
            z = Mathf.Clamp(z, 0, mapWidth);

            float targetX = Mathf.Clamp(x + sizeX, 0, mapWidth);
            float targetZ = Mathf.Clamp(z + sizeZ, 0, mapWidth);

            Transform managerTransform = m_FoliageInstance.transform;

            Vector3 rayPos = new Vector3(0, managerTransform.position.y + 10000, 0);
            Vector3 vDown = Vector3.down;

            FoliageHeightMap heightMap = this.HeightMap;
            Vector2b emptyHeight = NormalizeHeight(0);

            Color32 emptyColor = new Color32();
            for (float xIndex = x; xIndex < targetX; xIndex++)
            {
                for (float zIndex = z; zIndex < targetZ; zIndex++)
                {
                    rayPos.x = xIndex * resMultiplier + managerTransform.position.x; // transform to world cords
                    rayPos.z = zIndex * resMultiplier + managerTransform.position.z; // transform to world cords

                    RaycastHit hit;

                    Vector2b heights;

                    if (FoliageMapUtility.GetMapHit(rayPos, vDown, out hit, Mathf.Infinity))
                    {
                        heights = NormalizeHeight(hit.point.y);
                    }
                    else
                    {
                        heights = emptyHeight;
                    }

                    int index = (int) xIndex + (int) zIndex * mapWidth;

                    heightMap.SetHeightFast(index, emptyColor, heights.x, heights.y);
                }
            }

            if (isSave)
            {
                heightMap.SetPixels32();
            }

            heightMap.Dirty = true;
        }

        /// <summary>
        /// 更新整个图的高度和法线
        /// </summary>
        /// <param name="save"></param>
        public void UpdateHeightsAndNormals(bool save)
        {
            UpdateHeightsAndNormals(0, 0, m_FoliageInstance.AttachedFoliageInstance.FoliageAreaResolutionIntegral, m_FoliageInstance.AttachedFoliageInstance.FoliageAreaResolutionIntegral, save);
        }

        /// <summary>
        /// 根据高度图获取高度信息
        /// </summary>
        /// <param name="pixel"></param>
        /// <returns></returns>
        public float GetHeight(int index)
        {
            Color32 pixel = HeightMap.MapPixels[index];

            return (((pixel.r * 256f) + pixel.g) / 65535f) * Height_Resolution;
        }

        /// <summary>
        /// 设置高度信息
        /// </summary>
        /// <param name="index"></param>
        /// <param name="colorInstance"></param>
        /// <param name="h1"></param>
        /// <param name="h2"></param>
        public void SetHeightFast(int index, Color32 colorInstance, byte h1, byte h2)
        {
            HeightMap.SetHeightFast(index, colorInstance, h1, h2);
        }

        /// <summary>
        /// 保存世界图休息
        /// </summary>
        public void Save()
        {
            HeightMap.Save();
        }

        /// <summary>
        /// 延迟设置世界图信息
        /// </summary>
        public void SetPixels32Delayed()
        {
            HeightMap.SetPixels32Delayed();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return HeightMap == null;
            }

            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool GlobalDirty
        {
            get
            {
                FoliageManager manager = FoliageManager.Instance;
                FoliageChunk chunk;
                FoliageInstanceObject foliageInstance;

                FoliageSector sector = manager.Sector;

                for (int j = 0; j < sector.FoliageChunks.Count; j++)
                {
                    chunk = sector.FoliageChunks[j];

                    if (!chunk.IsFoliageInstanceAttached)
                    {
                        continue;
                    }

                    foliageInstance = chunk.GetOrCreateFoliageManagerInstance();

                    if (foliageInstance.WorldMaps.Dirty)
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        /// <summary>
        /// 重新生成地图数据
        /// </summary>
        public static void ReGenerateGlobally()
        {
            FoliageManager manager = FoliageManager.Instance;
            FoliageChunk chunk;
            FoliageInstanceObject foliageInstance;

            for (int j = 0; j < manager.Sector.FoliageChunks.Count; j++)
            {
                chunk = manager.Sector.FoliageChunks[j];

                if (!chunk.IsFoliageInstanceAttached)
                {
                    continue;
                }

                foliageInstance = chunk.GetOrCreateFoliageManagerInstance();

                foliageInstance.WorldMaps.UpdateHeightsAndNormals(true);
            }
        }

        /// <summary>
        /// 保存所有地图数据
        /// </summary>
        public static void SaveAllMaps()
        {
            FoliageManager manager = FoliageManager.Instance;
            FoliageChunk chunk;
            FoliageInstanceObject foliageInstance;

            for (int j = 0; j < manager.Sector.FoliageChunks.Count; j++)
            {
                chunk = manager.Sector.FoliageChunks[j];

                if (!chunk.IsFoliageInstanceAttached)
                {
                    continue;
                }

                foliageInstance = chunk.GetOrCreateFoliageManagerInstance();

                foliageInstance.WorldMaps.Save();
            }
        }

        public static void ApplyAreaSizeChange(FoliageInstanceObject foliageInstance)
        {
            foliageInstance.WorldMaps.HeightMap.Resize(foliageInstance.FoliageAreaResolutionIntegral);
            UNSettings.Log("World maps updated succesfully after modifying resolution.");
        }
    }
}
